import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;
import java.util.StringTokenizer;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

/**
 * Next time you modify code, *please* delete all the leftover crap that doesn't do anything anymore
 * @author flux
 */


public class Level {

	String lname, lnum, numeric_num;

	//Contains the level file's text, line by line.
	private ArrayList<String> fileText;

	//ArrayList containing each moving platform object
	ArrayList<MovingPlatform> movingPlatforms;

	//ArrayList containing each Laser wall object
	ArrayList<LaserWall> laserWalls;

	//ArrayList containing each Turret object
	ArrayList<Turret> turrets;

	//Start, finish location

	//Although these are Point2D.Doubles, player start and exit should still be integer values.
	Point2D.Double playerstart, playerexit, playervel;

	String nextLevel ;

	//should this be public? idts but idk yet
	private HashMap<Integer, ArrayList<Integer>> tilehash;
	
	
	private HashMap<Integer, ArrayList<Turret>> turretHash;

	//List of all tile coordinates (1,2,3...n listing used in the hashmap
	Point tileListing[];
	Rectangle[] tileCollisionRectangles;

	BufferedImage levelBackground;
	int numDraws;
	
	int framerate;



	/**
	 * This constructor is all that needs to be called in order to fully
	 * load the level. Nothing else needs to be done.
	 * @param filename
	 * @param framerate 
	 */
	public Level( String filename, int framerate ){
		this.framerate = framerate;
		numeric_num = filename.split("/")[filename.split("/").length-1];
		numeric_num = numeric_num.substring(0,1)+"."+numeric_num.substring(1);


		System.out.println("Numeric num:"+numeric_num);

		System.out.println("======== LEVEL STUFF =========");

		fileText = new ArrayList<String>();
		movingPlatforms=new ArrayList<MovingPlatform>();
		laserWalls=new ArrayList<LaserWall>();
		turrets=new ArrayList<Turret>();
		turretHash = new HashMap<Integer, ArrayList<Turret>>();
		//change

		try {
			//Load in level data into an arraylist for further reading.
			System.out.println("Trying to load "+filename);
			System.out.println("Does it exist? "+(new File(filename).exists()));
			Scanner fin = new Scanner(new FileReader(filename));
			while(fin.hasNextLine()){
				fileText.add(fin.nextLine());

				//Remove comments and blank lines from leveldef
				if(fileText.get(fileText.size()-1).equals("") ||
						fileText.get(fileText.size()-1).startsWith("//"))

					fileText.remove(fileText.size()-1);
			}
		} catch (FileNotFoundException e) {
			System.out.println("Unable to read level definition!");
			JOptionPane.showMessageDialog(null, "AAAAAH level load failed.");
			e.printStackTrace();			
		}

		//Parse the level definition...

		for(int i = 0; i < fileText.size(); i++	){
			String line = fileText.get(i).trim();	

			System.out.println("Switching on: "+line.charAt(0));
			switch(line.charAt(0)){
			case 'M':
				//x,y,x',y'
				int mpoints[] = parseMeSomeInts(line.substring(2).trim());
				//TODO:Finish this, getting Moving platforms from level text

				movingPlatforms.add(new MovingPlatform(
						mpoints[0],mpoints[1],mpoints[2],mpoints[3],mpoints[4],mpoints[5]/*sec*/, framerate/*fps*/						
				));

				break;
			case 'T':

				line=line.substring(2);

				//if necessary, truncate the last comma
				if(line.charAt(line.length()-1) == ',')
					line = line.substring(0,line.length()-1);

				String coord[]= line.split(",");
				tileListing =new Point[(coord.length)];

				for(int i2=0;i2<coord.length;i2++){

					int []temp=parseMeSomeInts(coord[i2]);
					Point p=new Point(temp[0],temp[1]);
					tileListing[i2]=p;

				}

				break;

			case 'A':

				line=line.substring(2); 

				String laserc[]= line.split(",");
								
				for(String laserDef: laserc){
					int []ld=parseMeSomeInts(laserDef);
					
					//Position, seconds between state change, framerate
					laserWalls.add(new LaserWall(new Point(ld[0],ld[1]),ld[2],framerate));
				}
				break;
			case 'R':

				line=line.substring(2); 
				System.out.println(line);
			//	String turretc[]= line.split(",");
								
				//for(String turretDef: turretc){
					int []td=parseMeSomeInts(line);
					
					//Position, seconds between state change, framerate
			
					if(turretHash.containsKey(genKeyTile(td[0], td[1]))){
				turretHash.get( genKeyTile(td[0], td[1])).add( new Turret(new Point(td[0],td[1])));
				System.out.println("adding to existing turret array");
				
					}
				else{
					turretHash.put(genKeyTile(td[0], td[1]),new ArrayList<Turret>());	
					turretHash.get( genKeyTile(td[0], td[1])).add( new Turret(new Point(td[0],td[1])));
					System.out.println("making new Turret Array");
				}
					System.out.println("Turret: "+turretHash.get(genKeyTile(td[0], td[1])).size());
				
				
				break;

			case 'P': //player start info
				if(line.charAt(1)=='S'){
					int[]ps = parseMeSomeInts(line.substring(3).trim());
					playerstart = new Point2D.Double(ps[0],ps[1]);
					playervel = new Point2D.Double( ps[2], ps[3]);
					System.out.println(Arrays.toString(ps)+" is PS");

				}else if (line.charAt(1)=='E') {
					int[]pe = parseMeSomeInts(line.substring(3).trim());
					playerexit = new Point2D.Double(pe[0],pe[1]);
					System.out.println(Arrays.toString(pe)+" is PE");
				}

				break;

			case 'L':

				if(line.substring(0, line.indexOf(" ")).trim().equals("LNAME")){
					lname = line.substring(line.indexOf(" ")).trim();
				}else if(line.substring(0, line.indexOf(" ")).trim().equals("LNUM")){
					lnum = line.substring(line.indexOf(" ")).trim();
				}
				break;

			case 'I': //Image tag
				System.out.println("Found an \"I\" tag on line "+(i+1)+":");
				System.out.println(line);
				if(line.substring(0, line.indexOf(" ")).trim().equals("IBG")){ //bg tag...

					try {

						levelBackground = ImageIO.read(	new File("Levels/"+ line.split(" ")[1])	);
						System.out.println("BG image succesfffuly loaded! [Apparently]");

					} catch (IOException e) {
						System.err.println("Could not load background image!");
						JOptionPane.showMessageDialog(null, "couldn't load bg image...");
						e.printStackTrace();
					}

				}

				break;

			case 'N':  //The "link" to the next level after this
				if(line.substring(0, line.indexOf(" ")).trim().equals("NL")){ //bg tag...
					nextLevel = line.split(" ")[1];
					if(nextLevel.equals("DNE"))
						nextLevel = null;
				}else if(line.substring(0, line.indexOf(" ")).trim().equals("NUMDRAWS")){ //bg tag...

					numDraws = Integer.parseInt( line.split(" ")[1]);

				}
				break;

			default:

				System.out.println("WARNING: Could not parse line "+i+": "+line);
				break;
			}

		}

		generateHash();

		generateRectanglesFromCoords();


		System.out.println("Entering level "+lname);
		System.out.println("Level number "+lnum);
		System.out.println("You have "+numDraws+" draws");
		System.out.println("The next level is: "+nextLevel);

		System.out.println("======== //LEVEL STUFF =========");

	}


	//Generate rectangle objects for collision .intersects(rect) operations
	//based on the tile listing (the 0,1,2,...n thing)
	private void generateRectanglesFromCoords() {
		tileCollisionRectangles = new Rectangle[tileListing.length];

		for(int i=0;i<tileCollisionRectangles.length;i++){
			tileCollisionRectangles[i] = new Rectangle(
					tileListing[i].x*40, tileListing[i].y*40, 40, 40);			
		}

	}

	private void generateHash(){
		tilehash = new HashMap<Integer, ArrayList<Integer>>();
		for(int c=0;c<tileListing.length;c++){
			int tempkey=0;
			tempkey=(int)((1.0*(tileListing[c].x))/4)+100*((int)(((1.0*tileListing[c].y))/4));
			//	System.out.println(tempkey);
			if(tilehash.containsKey(tempkey)){

				tilehash.get(tempkey).add(c);
				//				tilehash.get(tempkey).add(new HashIndexer<Integer>(c));
			}
			else{
				tilehash.put(tempkey, new ArrayList<Integer>());
				tilehash.get(tempkey).add(c);
			}
		}

		//		System.out.println("hash "+tilehash.get(202).get(0));
		//		System.out.println("hash2 "+tilehash.get(202).get(1));

		//for(int tilenumj: tilehash.get(4)){
		//	System.out.println(tilenumj);
		//}

	}
	/**
	 * Get the indexes for all the objects in the surrounding
	 * (and current) grid locations of the player
	 * @param x
	 * @param y
	 * @return
	 */
	public ArrayList<Integer> adjPlace(double x, double y){
		ArrayList<Integer> adj= new ArrayList<Integer>();
		int playerKey=(int)((1.0*(x))/160)+100*((int)(((1.0*y)/160)));
		//		System.out.println("playerkey"+playerKey);
		if(tilehash.containsKey(playerKey)){

			adj.addAll(tilehash.get(playerKey));
		}

		if(tilehash.containsKey(playerKey-1)){

			adj.addAll(tilehash.get(playerKey-1));
		}

		if(tilehash.containsKey(playerKey-1+100)){
			adj.addAll(tilehash.get(playerKey-1+100));
		}

		if(tilehash.containsKey(playerKey+100)){
			adj.addAll(tilehash.get(playerKey+100));
		}

		if(tilehash.containsKey(playerKey+100+1)){
			adj.addAll(tilehash.get(playerKey+1+100));
		}

		if(tilehash.containsKey(playerKey+1)){
			adj.addAll(tilehash.get(playerKey+1));
		}

		if(tilehash.containsKey(playerKey+1-100)){
			adj.addAll(tilehash.get(playerKey-100+1));
		}

		if(tilehash.containsKey(playerKey-100)){
			adj.addAll(tilehash.get(playerKey-100));
		}

		if(tilehash.containsKey(playerKey-1-100)){
			adj.addAll(tilehash.get(playerKey-1-100));
		}
		return adj;

	}

	//Takes a string of space-delimited integers and returns an array with each integer
	//in an array.
	private static int[] parseMeSomeInts(String s){

		StringTokenizer st = new StringTokenizer(s+" ");
		int[] nums = new int[st.countTokens()];

		for(int i = 0; 0 < st.countTokens() ; i++)
			nums[i] = Integer.parseInt(st.nextToken());		
		return nums;

	}


	public int genKeyTile(int xx, int yy){
		return(int)((1.0*(xx))/4)+100*((int)(((1.0*yy))/4));
	}




}
